### 第05课 舵机控制

#### 5.1 项目介绍：

本教程将为您详细介绍舵机模块的使用方法和应用技巧。舵机是一种能够精确控制角度的电机，广泛应用于机器人、遥控模型和自动化设备中。教程内容包括舵机的工作原理、引脚功能、电路连接方法以及如何通过微控制器控制舵机的转动角度。

#### 5.2 元件知识：

![](../../img/image19.png)

**什么是舵机？** 

舵机（Servo）是一种可以精确控制旋转角度的电机。它就像一个听话的机械手臂关节，你可以命令它转到特定的角度（比如 0°、90° 或 180°），并且它能稳稳地停在那里。

**舵机的内部构造：** 舵机主要由外壳、电路板、无核心马达、齿轮组和位置检测器组成。

**它的工作原理是：** 单片机（如 Arduino）发出信号给舵机，舵机内部的电路会将接收到的信号与当前位置进行比较，驱动马达转动，直到到达指定的角度为止。


**舵机的接线：** 虽然不同品牌的舵机颜色可能略有差异，但大多数标准舵机都有三根线，功能如下：

![](../../img/image20.png)

- 棕色线：接地线（GND），连接电源负极。

- 红色线：电源线（VCC），连接电源正极。

- 橙色线（或黄色）：信号线（Signal），连接单片机的数字引脚，用于接收控制指令。

**舵机是如何转动的？** 

舵机的转动角度是通过调节 PWM（脉冲宽度调制） 信号的占空比来实现的。

例如（这里以180°舵机为例）：

- 标准信号周期：固定为 20ms（即频率为 50Hz）。

- 脉宽与角度的关系：
   
   - 脉宽 0.5ms ~ 2.5ms 对应舵机转角 0° ~ 180°。
   
   - 通常，1.5ms 的脉宽对应 90°（中间位置）。

![](../../img/image21.png)

具体的对应关系可以参考下表：

![](../../img/image22.png)

**舵机参数：**

- 工作电压：DC 4.8V〜6V

- 可操作角度范围：大约 0 - 180°(在 500→2500 μsec)

- 脉波宽度范围：500 → 2500 μsec

- 空载转速：0.12±0.01 sec/60（DC 4.8V）， 0.1±0.01 sec/60（DC 6V）

- 空载电流：200±20mA（DC 4.8V）， 220±20mA（DC 6V）

- 停止扭力：1.3±0.01kg·cm（DC 4.8V），1.5±0.1kg·cm（DC 6V）

- 停止电流：≦850mA（DC 4.8V）， ≦1000mA（DC 6V）

- 待机电流：3±1mA（DC 4.8V）， 4±1mA（DC 6V）

#### 5.3 项目组件

| 组装好的智能车(<span style="color: rgb(255, 76, 65);">未插上蓝牙模块</span>) *1 | USB线 *1 | 5号(1.5V)电池 *6（电池自备） |
| --- | --- | --- | 
| ![](../../img/image4.png) | ![](../../img/image8.png)| ![](../../img/image11.png) |

#### 5.4 接线图：

⚠️ 特别注意：4WD智能车已经组装好了，这里不需要把舵机拆下来又重新组装和接线，这里再次提供接线图，是为了方便您编写代码！

| 舵机 | 电机驱动扩展板 | 
| :--: | :--: | 
| 棕色线 | G |
| 红色线 | 5V |
| 橙色线 | S（D10）| 

![](../../img/image23.png)

⚠️ <span style="color: rgb(255, 76, 65);">**特别注意：**</span>

- 接线时请确保电源断开(拔掉Arduino主控板上的USB线或将电机驱动扩展板上的拨码开关拨到 “<span style="color: rgb(255, 76, 65);">**OFF**</span>” 端)，避免短路。

- 舵机在转动瞬间需要的电流较大（峰值可能接近 1A），而 Arduino 开发板提供的电流较小。如果直接用开发板供电，可能会导致开发板重启或损坏。因此，强烈建议使用外接电源。

- 电源连接：电池盒电源接到电机驱动扩展板的 BAT 接口（注意正负极不要接反），端口正反面，请勿反插，否则会损坏端口。

- 电池正负极切勿接反，否则可能烧毁电机驱动扩展板。

- 电机驱动扩展板上的拨码开关拨到 “<span style="color: rgb(255, 76, 65);">**ON**</span>” 端。



#### 5.5 示例代码1：手动编写 PWM 信号控制

为了让你理解舵机控制的底层原理，我们先不使用库文件，而是通过手动模拟 PWM 信号来控制舵机。

⚠️ <span style="color: rgb(255, 76, 65);">**重要提示：**</span>

- <span style="color: rgb(172, 57, 255);">**上传示例代码前，请务必拔掉蓝牙模块！ 因为蓝牙模块也占用Arduino的串口通信（TX/RX），如果不拔掉，示例代码上传会失败。**</span>

```cpp
/*
  keyes 4WD 多功能智能车
  课程 05.1
  舵机控制
  http://www.keyes-robot.com
*/
#define SERVO_PIN 10  // 舵机引脚

int pos;           // 舵机角度变量
int pulseWidth;    // 舵机脉宽变量

/* 功能：初始化设置 */
void setup() {
  pinMode(SERVO_PIN, OUTPUT);  // 设置舵机引脚为输出
  setServoAngle(0);            // 设置舵机角度为0度
}

/* 功能：主循环，舵机从0度转到180度再转回0度 */
void loop() {
  for (pos = 0; pos <= 180; pos += 1) {  // 从0度转到180度，步进1度
    setServoAngle(pos);                   // 设置舵机角度
    delay(15);                           // 控制舵机转动速度
  }
  for (pos = 180; pos >= 0; pos -= 1) {  // 从180度转回0度，步进1度
    setServoAngle(pos);                   // 设置舵机角度
    delay(15);                           // 控制舵机转动速度
  }
}

/* 功能：根据角度设置舵机脉宽信号 */
void setServoAngle(int angle) {
  pulseWidth = angle * 11 + 500;          // 计算脉宽值，单位微秒
  digitalWrite(SERVO_PIN, HIGH);          // 发送高电平
  delayMicroseconds(pulseWidth);          // 高电平持续时间即脉宽
  digitalWrite(SERVO_PIN, LOW);           // 发送低电平
  delay(20 - pulseWidth / 1000);          // 低电平持续时间，周期20ms
}
```

#### 5.6 项目结果1：

⚠️ <span style="color: rgb(255, 76, 65);">**重要提示：**</span>

- <span style="color: rgb(172, 57, 255);">**上传示例代码前，请务必拔掉蓝牙模块！ 因为蓝牙模块也占用Arduino的串口通信（TX/RX），如果不拔掉，示例代码上传会失败。**</span>

外接电源，将电机驱动扩展板上的拨码开关拨到 “<span style="color: rgb(255, 76, 65);">**ON**</span>” 端，上电后。选择好正确的开发板板型（Arduino Uno）和 适当的串口端口（COMxx），然后单击 ![Arduino_2.3.6-a40](../../img/Arduino_2.3.6-a40.png) 按钮上传示例代码1至Arduino控制板。

代码上传成功后，可以看到舵机在0°到180°角度范围来回摆动。

![](../../img/A15.gif)

#### 5.7 示例代码2：使用 Servo 库控制（推荐方法）

手动编写 PWM 信号比较麻烦且容易出错。Arduino 官方提供了一个专门的 Servo 库，可以极大地简化代码。这是我们在实际制作中最常用的方法。

**以下是使用了舵机库文件的程序，接线图保存不变：**

![](../../img/image23.png)

⚠️ <span style="color: rgb(255, 76, 65);">**重要提示：**</span>

- 1\. 你需要安装 `<Servo.h>` 库才能编译此代码，如果已经安装过，直接跳过。
   
- 2\. <span style="color: rgb(172, 57, 255);">**上传示例代码前，请务必拔掉蓝牙模块！ 因为蓝牙模块也占用Arduino的串口通信（TX/RX），如果不拔掉，示例代码上传会失败。**</span>

```cpp
/*
  keyes 4WD 多功能智能车
  课程 05.2
  舵机控制
  http://www.keyes-robot.com
*/
#include <Servo.h>

#define SERVO_PIN 10  // 舵机信号引脚

Servo myServo;  // 创建舵机对象控制舵机
int pos = 0;    // 舵机位置变量

/* 功能：初始化舵机 */
void setup() {
  myServo.attach(SERVO_PIN);  // 绑定舵机信号引脚
}

/* 功能：循环控制舵机从 0 度转到 180 度再回到 0 度 */
void loop() {
  for (pos = 0; pos <= 180; pos += 1) {  // 从 0 度转到 180 度，步进 1 度
    myServo.write(pos);                   // 设置舵机位置
    delay(15);                           // 等待舵机到达位置
  }
  for (pos = 180; pos >= 0; pos -= 1) {  // 从 180 度转回 0 度，步进 1 度
    myServo.write(pos);                   // 设置舵机位置
    delay(15);                           // 等待舵机到达位置
  }
}
```

#### 5.8 项目结果2：

⚠️ <span style="color: rgb(255, 76, 65);">**重要提示：**</span>

- <span style="color: rgb(172, 57, 255);">**上传示例代码前，请务必拔掉蓝牙模块！ 因为蓝牙模块也占用Arduino的串口通信（TX/RX），如果不拔掉，示例代码上传会失败。**</span>

外接电源，将电机驱动扩展板上的拨码开关拨到 “<span style="color: rgb(255, 76, 65);">**ON**</span>” 端，上电后。选择好正确的开发板板型（Arduino Uno）和 适当的串口端口（COMxx），然后单击 ![Arduino_2.3.6-a40](../../img/Arduino_2.3.6-a40.png) 按钮上传示例代码2至Arduino控制板。

代码上传成功后，舵机也是在0°到180°角度范围来回摆动。

![](../../img/A15.gif)

⚠️ <span style="color: rgb(255, 76, 65);">**特别提醒：这两个项目的效果是一样的 ，通常我们使用库文件来控制的比较多。**</span>

#### 5.9 代码说明:

```C
delayMicroseconds()
```
这个函数可以产生微秒级的延时，用来精确控制高电平的持续时间（脉宽）

```C
setServoAngle
```
这是核心部分，它根据输入的角度计算出需要的高电平时间，然后发送信号。

```C
delay(20 - pulsewidth / 1000)
```

PWM 信号的周期必须是 20ms。既然高电平已经占用了一部分时间，剩下的时间必须拉低电平，以凑够 20ms 的总周期。

```C
#include <Servo.h>
```

这行代码告诉 Arduino 我们要使用舵机库。

```C
Servo myservo;
```
创建一个 “舵机” 对象，以后我们就通过`myservo`来控制舵机。

```C
myservo.write(pos);
```
这是最简单的指令，直接写入角度值（0-180），库文件会自动处理复杂的 PWM 信号生成。


#### 5.10 注意事项与常见问题

1\. 供电不足：如果发现舵机抖动、无力或 Arduino 频繁重启，请检查外接电池是否有电，以及接线是否牢固。切勿仅依靠 USB 供电驱动大扭矩舵机。

2\. 角度限制：大多数普通舵机（如 SG90）的物理旋转范围略小于 180°（通常在 0-180° 之间，但两端可能有几度的死区）。如果写入 0° 或 180° 时听到舵机发出“咯咯”的挣扎声，请尝试改为 5° 和 175°。

